﻿const files         = require('fs');
const types         = require("@babel/types");
const parser        = require("@babel/parser");
const traverse      = require("@babel/traverse").default;
const generator     = require("@babel/generator").default;
const template      = require("@babel/template");
const usefulPlugins = require("./tools/usefulPlugins");

//js混淆代码读取
process.argv.length > 2 ? encodeFile = process.argv[2] : encodeFile = "./encode.js";  //默认的js文件
process.argv.length > 3 ? decodeFile = process.argv[3] : decodeFile = encodeFile.slice(0, encodeFile.length - 3) + "_ok.js";

//将源代码解析为AST
let sourceCode = files.readFileSync(encodeFile, { encoding: "utf-8" });
let ast = parser.parse(sourceCode);

//以下代码勿动。。。

let  decryptCode = files.readFileSync("decodeFunc.js", { encoding: "utf-8" });
if (decryptCode.length == 0 || !decryptCode.includes("function")) {
	
	traverse(ast, standardLoop);
	
	traverse(ast, resolveSequence);
	
	traverse(ast, simplifyLiteral);

  let { code } = generator(ast, opts = { jsescOption: { "minimal": true } });

  let fd = files.openSync("beautify.js", "w");

  files.writeSync(fd, code);

  files.closeSync(fd);

  console.log("\n已将代码格式化并已保存为beautify.js，\n请抠出解密函数至decodeFunc.js文件后再次运行！\n");

  process.exit(1);

}

console.time("处理完毕，耗时");



function getDecryptFuncNames(decryptCode) {

  let decryptAst = parser.parse(decryptCode);
  let funcNames = [];
  traverse(decryptAst, {
    "FunctionDeclaration|VariableDeclarator"(path) {

      let { id, init, params } = path.node;
      if (path.isVariableDeclarator()) {
        if (!types.isFunctionExpression(init)) {
          return;
        }
        params = init.params;
      }

      if (params.length == 0) {
        return;
      }

      let hasRet = false;
      path.traverse({
        ReturnStatement(_path) {
          hasRet = true;
          _path.stop();
        },
      });
      if (hasRet) {
        funcNames.push(id.name);
      }
    },
  });

  return funcNames;
}



let   funcNames = getDecryptFuncNames(decryptCode); //自动获取解密函数名
const funcLen   = funcNames.length;




const collectAllFuncs =
{//仅针对新版ob混淆
  FunctionDeclaration(path) {
    let { id, body, params } = path.node;

    if (params.length < 2) {
      return;
    }
    body = body.body;
    if (!body || body.length != 1 || !types.isReturnStatement(body[0])) {
      return;
    }
    let argument = body[0].argument;
    if (!types.isCallExpression(argument) ||
        !types.isIdentifier(argument.callee)) {
      return;
    }

    decryptCode += "\n" + path.toString();
    funcNames.push(id.name);
  }
}


traverse(ast, collectAllFuncs);


let evalAst = parser.parse(decryptCode);

decryptCode = generator(evalAst, opts = { compact: true }).code;

eval(decryptCode);


const deleteRepeatDefineOfFunction =
{
  VariableDeclarator(path) {
    let { node, scope } = path;
    let { id, init } = node;
    if (!types.isIdentifier(init) || !funcNames.includes(init.name)) {
      return;
    }

    let funcId = types.Identifier(init.name);

    const binding = scope.getBinding(id.name);
    
    if (!binding) return;

    let { constant, referencePaths, constantViolations } = binding;

    if (!constant) {//新版本的babel库，在循环里面的变量定义，默认非常量
      if (constantViolations.length != 1 || constantViolations[0] != path) //旧版本屏蔽该行即可
      {
        return;
      }
    }

    for (let referPath of binding.referencePaths) {
      referPath.replaceWith(funcId);//使用replaceWith函数比rename函数更快。
    }
    
    path.remove();
    
    scope.crawl();
  },

}


traverse(ast, deleteRepeatDefineOfFunction);

function isNodeLiteral(node) {//判断形参是否全部为字面量，要不然eval的时候会报错
  if (Array.isArray(node)) {
    return node.every(ele => isNodeLiteral(ele));
  }
  if (types.isLiteral(node)) {
    if (node.value == null) {
      return false;
    }
    return true;
  }
  if (types.isBinaryExpression(node)) {
    return isNodeLiteral(node.left) && isNodeLiteral(node.right);
  }
  if (types.isUnaryExpression(node, {
    "operator": "-"
  }) || types.isUnaryExpression(node, {
    "operator": "+"
  })) {
    return isNodeLiteral(node.argument);
  }

  if (types.isObjectExpression(node)) {
    let { properties } = node;
    if (properties.length == 0) {
      return true;
    }

    return properties.every(property => isNodeLiteral(property));

  }
  if (types.isArrayExpression(node)) {
    let { elements } = node;
    if (elements.length == 0) {
      return true;
    }
    return elements.every(element => isNodeLiteral(element));
  }

  return false;
}

const callToString = {

  "CallExpression"(path) {
    let node = path.node;
    let { callee, arguments } = node;

    if (!types.isIdentifier(callee) || !funcNames.includes(callee.name)) {
      return;
    }
    
    if (arguments.length == 0 || !isNodeLiteral(arguments)) {
      return;
    }
    try {
      let value = eval(path.toString());
      console.log(path.toString(), "--->", value);
      path.replaceWith(types.valueToNode(value));
    } catch (e) { }
  },
}

traverse(ast, callToString);

traverse(ast, constantFold);



if (funcNames.length > funcLen) {
	let traverseFlag = false;
  const removeDeadFunctionDeclaration =
  {
    FunctionDeclaration(path) {
    	traverseFlag = false;
      let { parentPath, node } = path;
      if (parentPath.isProgram()) {
        return;//全局函数不作处理
      }

      if (!funcNames.includes(node.id.name)) {
        return;
      }

      let body = node.body.body;
      if (!body || body.length != 1 || !types.isReturnStatement(body[0])) {
        return;
      }
      let argument = body[0].argument;
      if (!types.isCallExpression(argument) ||
          !types.isIdentifier(argument.callee)) {
        return;
      }
      let binding = parentPath.scope.getBinding(node.id.name);
      let isReferenced = false;
      for (let referPath of binding.referencePaths) {
        if (!path.isAncestor(referPath)) {
          isReferenced = true;
          break;
        }
      }
      if (!isReferenced) {
        console.log(path.toString());
        traverseFlag = true;
        path.remove();

      }
    }
  }
  do{
  	ast = parser.parse(generator(ast).code);
    traverse(ast, removeDeadFunctionDeclaration);
  }while(traverseFlag)

}




traverse(ast, simplifyLiteral);

console.timeEnd("处理完毕，耗时");


let { code } = generator(ast, opts = { 
	"compact"     : false,  // 是否压缩代码
  "comments"    : false,  // 是否保留注释
	"jsescOption" : { "minimal": true },  //Unicode转义
});

files.writeFile(decodeFile, code, (err) => { });